package com.treasure.infrastructure.domain.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.treasure.common.enums.SmsUsageEnum;
import com.treasure.common.exception.ParameterException;
import com.treasure.common.exception.ServiceException;
import com.treasure.common.types.Corp;
import com.treasure.domain.domain.MessageSenderDomainService;
import com.treasure.domain.domain.TextVariablesCombinedDomainService;
import com.treasure.domain.domain.entity.EmailMessage;
import com.treasure.domain.domain.entity.VerifyMessageContext;
import com.treasure.domain.domain.entity.document.SmsMessage;
import com.treasure.infrastructure.domain.impl.client.EmailSenderClient;
import com.treasure.infrastructure.mapstruct.SmsVerifyCodeInfoAssembler;
import com.treasure.infrastructure.model.po.CorpInfo;
import com.treasure.infrastructure.model.po.SmsVerifyCodeInfo;
import com.treasure.infrastructure.repository.dao.mapper.CorpInfoMapper;
import com.treasure.infrastructure.repository.dao.mapper.SmsVerifyCodeInfoMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Objects;
import java.util.Random;

/**
 * @author sunhongmin
 * @date 2023/11/5 11:41
 * @description
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class MessageSenderDomainServiceImpl implements MessageSenderDomainService {

    public static final Integer EMAIL_CODE_VALID_MINUTE = 5;
    public static final Integer SMS_CODE_VALID_MINUTE = 1;
    private final EmailSenderClient emailSenderClient;
    private final SmsVerifyCodeInfoMapper smsVerifyCodeInfoMapper;
    private final TextVariablesCombinedDomainService textVariablesCombinedDomainService;
    private final CorpInfoMapper corpInfoMapper;


    @Override
    public void sendEmailMessage(String invitationCode, EmailMessage emailMessage, SmsUsageEnum smsUsage) throws IOException {
        emailMessage.validate();
        // 获取有效期内验证码信息
        SmsVerifyCodeInfo smsVerifyCodeInfo = getSmsVerifyCodeInfoValidated(emailMessage.getReceiver(), smsUsage);
        if (Objects.nonNull(smsVerifyCodeInfo)) {
            throw new ParameterException("验证码已发送，请勿重复发送");
        }
        String verifyCode = getRandomVerifyCode();
        String messageContent = textVariablesCombinedDomainService.verifyMessageTemplateWithEmail(new VerifyMessageContext(verifyCode, EMAIL_CODE_VALID_MINUTE));
        log.info("sendEmailMessage: " + messageContent);

        if (StringUtils.isNotBlank(invitationCode)) {
            QueryWrapper<CorpInfo> corpInfoQueryWrapper = new QueryWrapper<>();
            corpInfoQueryWrapper.lambda()
                    .eq(CorpInfo::getInviteCode, invitationCode);
            CorpInfo corpInfo = corpInfoMapper.selectOne(corpInfoQueryWrapper);
            if (Objects.isNull(corpInfo)) {
                throw new ServiceException("未能通过企业/高校认证码获取到主体信息，请重新尝试通过特定入口跳转登录");
            }
            emailSenderClient.sendTextMailMessage(new Corp(corpInfo.getCorpId()), Collections.singletonList(emailMessage.getReceiver()), "【易智】Please check your verification code ", messageContent);
        } else {
            // 将使用系统默认邮箱进行发送
            emailSenderClient.sendTextMailMessage(Collections.singletonList(emailMessage.getReceiver()), "【易智】Please check your verification code ", messageContent);
        }
        // 获取接收方验证码发送记录
        SmsVerifyCodeInfo smsVerifyCodeInfoByPhone = getSmsVerifyCodeInfoExists(emailMessage.getReceiver());
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime oneMinuteLater = now.plusMinutes(EMAIL_CODE_VALID_MINUTE);
        if (Objects.isNull(smsVerifyCodeInfoByPhone)) {
            insert(emailMessage.getReceiver(), smsUsage, verifyCode, now, oneMinuteLater);
        } else {
            update(verifyCode, smsVerifyCodeInfoByPhone, now, oneMinuteLater);
        }
    }

    @Override
    public void sendSmsMessage(SmsMessage smsMessage, SmsUsageEnum smsUsage) throws IOException {
        smsMessage.validate();
        // 获取有效期内验证码信息
        SmsVerifyCodeInfo smsVerifyCodeInfo = getSmsVerifyCodeInfoValidated(smsMessage.getReceiver(), smsUsage);
        if (Objects.nonNull(smsVerifyCodeInfo)) {
            throw new ParameterException("验证码已发送，请勿重复发送");
        }

        String verifyCode = "yz123";
        // String verifyCode = getRandomVerifyCode();
        String messageContent = textVariablesCombinedDomainService.verifyMessageTemplateWithSms(new VerifyMessageContext(verifyCode, SMS_CODE_VALID_MINUTE));
        log.info("sendSmsMessage: " + messageContent);

        // 获取接收方验证码发送记录
        SmsVerifyCodeInfo smsVerifyCodeInfoByPhone = getSmsVerifyCodeInfoExists(smsMessage.getReceiver());
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime oneMinuteLater = now.plusMinutes(SMS_CODE_VALID_MINUTE);
        if (Objects.isNull(smsVerifyCodeInfoByPhone)) {
            insert(smsMessage.getReceiver(), smsUsage, verifyCode, now, oneMinuteLater);
        } else {
            update(verifyCode, smsVerifyCodeInfoByPhone, now, oneMinuteLater);
        }
    }

    private String getRandomVerifyCode() {
        Random random = new Random();
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            result.append(random.nextInt(10));
        }
        return result.toString();
    }


    private SmsVerifyCodeInfo getSmsVerifyCodeInfoExists(String receiver) {
        QueryWrapper<SmsVerifyCodeInfo> query = new QueryWrapper<>();
        query.eq("phone_number", receiver);
        return smsVerifyCodeInfoMapper.selectOne(query);
    }

    private SmsVerifyCodeInfo getSmsVerifyCodeInfoValidated(String receiver, SmsUsageEnum smsUsage) {
        QueryWrapper<SmsVerifyCodeInfo> smsVerifyCodeInfoQuery = new QueryWrapper<>();
        smsVerifyCodeInfoQuery.eq("phone_number", receiver)
                .eq("uses", smsUsage.getCode())
                .gt("expiration_time", LocalDateTime.now());
        return smsVerifyCodeInfoMapper.selectOne(smsVerifyCodeInfoQuery);
    }

    private void update(String verifyCode, SmsVerifyCodeInfo smsVerifyCodeInfoByPhone, LocalDateTime now, LocalDateTime oneMinuteLater) {
        SmsVerifyCodeInfo smsUpdateInfo = getSmsVerifyCodeUpdateInfo(verifyCode, smsVerifyCodeInfoByPhone, now, oneMinuteLater);
        smsVerifyCodeInfoMapper.updateById(smsUpdateInfo);
    }

    private void insert(String receiver, SmsUsageEnum smsUsage, String verifyCode, LocalDateTime now, LocalDateTime oneMinuteLater) {
        SmsVerifyCodeInfo insert = getSmsVerifyCodeInfo(receiver, smsUsage, verifyCode, now, oneMinuteLater);
        smsVerifyCodeInfoMapper.insert(insert);
    }

    @NotNull
    private static SmsVerifyCodeInfo getSmsVerifyCodeUpdateInfo(String verifyCode, SmsVerifyCodeInfo smsVerifyCodeInfoByPhone, LocalDateTime now, LocalDateTime oneMinuteLater) {
        SmsVerifyCodeInfo smsUpdateInfo = SmsVerifyCodeInfoAssembler.INSTANCE.toSmsVerifyCodeInfo(smsVerifyCodeInfoByPhone);
        smsUpdateInfo.setVerifyCode(verifyCode);
        smsUpdateInfo.setExpirationTime(oneMinuteLater);
        smsUpdateInfo.setCreateTime(now);
        smsUpdateInfo.setNumberOfRequest(smsUpdateInfo.getNumberOfRequest() + 1);
        return smsUpdateInfo;
    }

    @NotNull
    private static SmsVerifyCodeInfo getSmsVerifyCodeInfo(String receiver, SmsUsageEnum smsUsage, String verifyCode, LocalDateTime now, LocalDateTime oneMinuteLater) {
        SmsVerifyCodeInfo insert = new SmsVerifyCodeInfo();
        insert.setPhoneNumber(receiver);
        insert.setVerifyCode(verifyCode);
        insert.setExpirationTime(oneMinuteLater);
        insert.setCreateTime(now);
        insert.setNumberOfRequest(1);
        insert.setUses(smsUsage.getCode());
        return insert;
    }

}
